package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;


import com.alibaba.fastjson.*;
import com.aliyun.gts.bpaas.middleware.mq.api.model.MqPayload;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.FlowElementProcessContext;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.ModelDeploymentContext;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.MqMessageContext;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.ProcessItem;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.*;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.DeployIdContainer;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowCatalogueService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.ModelDeploymentPostProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.UserTaskManageService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.EventConfigPropertiesProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.MqProducer;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.*;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.common.utils.UUIDGenerator;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.app.FlowApp;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.eventconfig.NodeEventConfigRawDTO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowNoticeUserVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.statistics.ProcessInstanceStatisticsQueryVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.common.converter.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.*;
import jodd.util.collection.MapEntry;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.editor.constants.ModelDataJsonConstants;
import org.flowable.editor.language.json.converter.BpmnJsonConverterUtil;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.common.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.common.impl.cfg.IdGenerator;
import org.flowable.engine.impl.persistence.entity.ModelEntityImpl;
import org.flowable.engine.impl.repository.DeploymentBuilderImpl;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Service - 模型服务
 *
 * @author lilh
 * @date 2019-01-22 18:56
 */
@Service
@Slf4j
public class FlowModelRepositoryImpl implements FlowModelRepository {
    private static final Logger logger = LoggerFactory.getLogger(FlowModelRepositoryImpl.class);

    //流程删除
    public static final String TAG_DEL = "PROCESS-DELETE";

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FlowModelAppBusiRepository flowModelAppBusiRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private FlowAppRepository flowAppRepository;

    @Autowired
    private FlowBusiRepository flowBusiRepository;

    @Autowired
    private FlowModelProcdefRepository flowModelProcdefRepository;

    @Autowired
    private FlowModelQueryDAO flowModelQueryDAO;

    @Autowired
    private FlowProcdefQueryDAO flowProcdefQueryDAO;

    @Autowired
    private FlowThirdPlatformUrlRepository flowThirdPlatformUrlRepository;

    @Autowired
    private IdGenerator idGenerator;

    @Autowired
    private FlowProcessInstanceTraceRepository processInstanceTraceService;

    @Autowired
    private FlowRuleActionAuditLogDAO flowRuleActionAuditLogDAO;

    @Autowired
    private FlowCatalogueService catalogueService;

    @Autowired
    private FlowAppInfoDAO flowAppInfoDAO;

    @Autowired
    private FlowTenantInfoDAO flowTenantInfoDAO;

    @Autowired
    private List<ModelDeploymentPostProcessor> modelDeploymentPostProcessors;


    @Autowired
    private FlowFormRepository flowFormRepository;

    @Autowired
    private UserTaskManageService userTaskManageService;

    /**
     * 任务来源应用编码
     */
    @Value("${task.sourceAppCode:mes2}")
    private String sourceAppCode;

    @Override
    public List<ExternalFormInfoTransferDTO> findBindExternalFormWithModel(String modelId) {
        return flowFormRepository.findBindExternalFormWithModel(modelId);
    }

    @Override
    public boolean insertExternalFormBatch(List<ExternalFormInfoTransferDTO> externalFormInfoTransferDTOList) {
        return flowFormRepository.insertExternalFormBatch(externalFormInfoTransferDTOList);
    }


    /**
     * 检查"目标用户"是否在"当前环境+用户名name+用户编码code"下存在
     * TODO：改为通过 工号 形式
     *
     * @param target 目标查询用户
     * @return 是否查询到该用户
     */
    public boolean checkUserValid(User target) {
        User source = userRepository.getUserWithOrgByCodeFromUserCenter(target.getUserCode());
        // 是否需要判断用户所属的组织：source.getOrgCode().equals(target.getOrgCode())
        return source != null && source.getUserName().equals(target.getUserName())
                && source.getUserCode().equals(target.getUserCode());
    }

    /**
     * 节点下，usertaskassignment包含了candidateUsers+candidateGroups 或 assign的code内容
     * 无效用户清空后，这边对应也需要清空
     * 注意：首次保存模型时，usertaskassignment这个是后端生成的，不是前端传过来的，在userTaskAssignmentForFlow中
     *
     * @param missUsers  （不存在的用户/用户组）用户/用户组编码
     * @param properties
     */
    public void cleanInvalidAssignment(Set<String> missUsers, JSONObject properties) {
        if (missUsers.isEmpty()) {
            return;
        }
        JSONObject usertaskassignment = properties.getJSONObject("usertaskassignment");
        // 不存在，不处理
        if (usertaskassignment.isEmpty()) {
            return;
        }

        Set<String> codes = missUsers.stream().map(
                user -> user.substring(user.lastIndexOf("(") + 1, user.lastIndexOf(")"))
        ).collect(Collectors.toSet());
        JSONObject assignment = usertaskassignment.getJSONObject("assignment");
        // 处理 选中的一个办理人
        if (assignment.containsKey("assignee") && codes.contains(assignment.getString("assignee"))) {
            logger.info("指定办理人无效：{}", assignment.getString("assignee"));
            properties.put("usertaskassignment", "");
            return;
        }
        // 处理 候选组
        JSONArray candidateGroups = assignment.getJSONArray("candidateGroups");
        List<Map<String, String>> groups = JSON.parseObject(JsonUtils.toJson(candidateGroups), new TypeReference<List<Map<String, String>>>() {
        });
        if (Objects.nonNull(groups)) {
            Set<Map<String, String>> temp = groups.stream().filter(
                    group -> codes.contains(group.get("value"))
            ).collect(Collectors.toSet());
            logger.info("无效候选组：{}", temp);
            groups.removeAll(temp);
            assignment.put("candidateGroups", groups);
        }
        // 处理 候选人
        JSONArray candidateUsers = assignment.getJSONArray("candidateUsers");
        List<Map<String, String>> users = JSON.parseObject(JsonUtils.toJson(candidateUsers), new TypeReference<List<Map<String, String>>>() {
        });
        if (Objects.nonNull(users)) {
            Set<Map<String, String>> temp = users.stream().filter(
                    user -> codes.contains(user.get("value"))
            ).collect(Collectors.toSet());
            logger.info("无效候选人：{}", temp);
            users.removeAll(temp);
            assignment.put("candidateUsers", users);
        }
        usertaskassignment.put("assignment", assignment);
        properties.put("usertaskassignment", usertaskassignment);
    }

    /**
     * 检查"目标用户"是否在"当前环境"下存在
     * 通过 code+name 匹配
     *
     * @param param      参数
     * @param properties 属性
     * @return 无效的用户集
     */
    public Set<String> cleanInvalidUser(String param, JSONObject properties) {
        Set<String> missUsers = new HashSet<>();
        List<FlowNoticeUserVo> temp = new ArrayList<>();
        boolean valid = true;

        JSONArray jsonArray = properties.getJSONArray(param);
        List<FlowNoticeUserVo> noticeUsers = JSON.parseObject(JsonUtils.toJson(jsonArray), new TypeReference<List<FlowNoticeUserVo>>() {
        });
        if (Objects.nonNull(noticeUsers)) {
            // 遍历判断每一个用户
            for (FlowNoticeUserVo user : noticeUsers) {
                String type = user.getType();
                // 如果type是INIT,则说明是发起人
                if (ConstanceVariable.STARTER_TYPE.equals(type)) {
                    continue;
                }
                // 手动添加流程变量的办理人类型不做判断
                if (ConstanceVariable.CUSTOM_USER_TYPE.equals(type)) {
                    continue;
                }
                User target = new User();
                String name = Objects.nonNull(user.getUserName()) ? user.getUserName() : user.getName();
                String code = Objects.nonNull(user.getUserCode()) ? user.getUserCode() : user.getCode();
                target.setUserName(name);
                target.setUserCode(code);
                valid = checkUserValid(target);
                logger.info(String.format("用户[%s-%s]是否有效：%b", name, code, valid));
                if (!valid) {
                    missUsers.add(name + "(" + code + ")");
                    temp.add(user);
                }
            }
            // 如果无效，就要清空该部分
            noticeUsers.removeAll(temp);
            properties.put(param, noticeUsers);
        }
        return missUsers;
    }

    /**
     * 检查和清空"节点/任务-警报通知人"处的用户
     *
     * @param propertiesObject
     * @return 无效的用户集
     */
    public Set<String> checkWarningNotifier(JSONObject propertiesObject) {
        // 任务-警报通知人：timeoutSetting.warningSetting.warningRegular[x].warningNotifier.notifier，包含code、usercod、username、type、userOrgDesc
        JSONArray warnings = propertiesObject.getJSONObject("timeoutSetting").getJSONObject("warningSetting").getJSONArray("warningRegular");
        Set<String> missUsers = new HashSet<>();
        Set<String> temp;
        for (int i = 0; i < warnings.size(); i++) {
            JSONObject notifier = warnings.getJSONObject(i).getJSONObject("warningNotifier");
            temp = cleanInvalidUser("notifier", notifier);
            if (!temp.isEmpty()) {
                missUsers.addAll(temp);
                propertiesObject.getJSONObject("timeoutSetting")
                        .getJSONObject("warningSetting")
                        .getJSONArray("warningRegular")
                        .getJSONObject(i)
                        .put("warningNotifier", notifier);
            }
        }
        return missUsers;
    }

    /**
     * 检查和清空"节点/任务-超时通知人"处的用户
     *
     * @param propertiesObject
     * @return 无效的用户集
     */
    public Set<String> checkTimeoutNotifier(JSONObject propertiesObject) {
        Set<String> missUsers = new HashSet<>();
        Set<String> temp;
        // 节点-超时通知人：timeoutSetting.timeoutNotifier.notifier，包含code、usercode、username、type、userOrgDesc
        JSONObject notifier = propertiesObject.getJSONObject("timeoutSetting").getJSONObject("timeoutNotifier");
        temp = cleanInvalidUser("notifier", notifier);
        if (!temp.isEmpty()) {
            missUsers.addAll(temp);
            propertiesObject.getJSONObject("timeoutSetting").put("timeoutNotifier", notifier);
        }
        return missUsers;
    }

    /**
     * 遍历检查用户是否有效，无效就清空某个用户信息
     *
     * @return 无效的表单集合，通过 code+name 匹配。TODO：工号 形式(修改处：checkUserValid(User target))
     */
    public Set<String> doCheckInvalidUserInput(ProcessModelVo processModelVo) {
        Set<String> missUsers = new HashSet<>();
        // 外部: properties
        JSONObject propertiesObject = JSON.parseObject(JsonUtils.toJson(processModelVo.getProperties()));
        // 任务-抄送人：copyTo，包含code、usercode、name、username、type、userOrgDesc
        missUsers.addAll(cleanInvalidUser("copyTo", propertiesObject));
        // 任务-超时通知人：timeoutSetting.timeoutNotifier.notifier，包含code、usercode、username、type、userOrgDesc
        missUsers.addAll(checkTimeoutNotifier(propertiesObject));
        // 任务-警报通知人：timeoutSetting.warningSetting.warningRegular[x].warningNotifier.notifier，包含code、usercod、username、type、userOrgDesc
        missUsers.addAll(checkWarningNotifier(propertiesObject));
        // 更新更改
        processModelVo.setProperties(propertiesObject.toJavaObject(Object.class));

        // 检查各个节点properties: nodes[x].model.properties
        List<ModelNodeVo> nodes = processModelVo.getNodes();
        for (ModelNodeVo node : nodes) {
            if (!"UserTask".equals(node.getShape())) {
                continue;
            }
            logger.info("当前节点[校验用户]：label:{}, id:{}, index:{}", node.getLabel(), node.getId(), node.getIndex());
            // 每个node: properties
            propertiesObject = JSON.parseObject(JsonUtils.toJson(node.getModel().getProperties()));
            // 节点-处理人：candidateUsers，注意只包含code、name、type
            missUsers.addAll(cleanInvalidUser("candidateUsers", propertiesObject));
            // 节点-抄送人：copyTo，包含code、usercode、name、username、type、userOrgDesc
            missUsers.addAll(cleanInvalidUser("copyTo", propertiesObject));
            // 节点-超时通知人：timeoutSetting.timeoutNotifier.notifier，包含code、usercode、username、type、userOrgDesc
            missUsers.addAll(checkTimeoutNotifier(propertiesObject));
            // 节点-警报通知人：timeoutSetting.warningSetting.warningRegular[0].warningNotifier.notifier，包含code、usercode、username、type、userOrgDesc
            missUsers.addAll(checkWarningNotifier(propertiesObject));
            // 校验usertaskassignment
            cleanInvalidAssignment(missUsers, propertiesObject);
            // 更新更改
            node.getModel().setProperties(propertiesObject.toJavaObject(Object.class));
        }
        // 更新更改
        processModelVo.setNodes(nodes);
        return missUsers;
    }

    @Override
    public ResponseResult checkVariableByForm(String modelId) {
        // 根据模型id获取模型具体内容
        Model model = repositoryService.getModel(modelId);
        if (model == null) {
            logger.warn("流程模型不存在");
            return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        byte[] modelBytes = repositoryService.getModelEditorSource(model.getId());
        String editorSource = new String(modelBytes, StandardCharsets.UTF_8);

        FlowModelAppBusi modelAppBusiInfo = flowModelAppBusiRepository.getAppByModelId(modelId);
        FormQueryVO formQueryVO = new FormQueryVO();
        formQueryVO.setAppId(modelAppBusiInfo.getAppId());
        formQueryVO.setBusiId(modelAppBusiInfo.getBusiId());
        // 检查modelJson.properties
        JSONObject properties = JSON.parseObject(editorSource).getJSONObject("properties");
        // 流程变量存在最外面的properties下
        if (!properties.containsKey("modelVariables")) {
            logger.error("流程变量字段缺失");
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "流程变量字段缺失");
        }
        List<FormVariableVo> formVariablesList = JSONObject.parseObject(JsonUtils.toJson(
                properties.getJSONArray("modelVariables")), new TypeReference<List<FormVariableVo>>() {
        });
        if (Objects.isNull(formVariablesList)) {
            logger.error("流程变量字段解析失败");
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "流程变量字段解析失败");
        }
        // 对每个流程变量所属的表单进行存在性检查
        List<FormVariableVo> temp = new ArrayList<>();
        for (FormVariableVo item : formVariablesList) {
            String source = item.getVariableSource();
            // 来源于表单的变量才要校验
            if (!"form".equals(source)) {
                continue;
            }
            String formName = item.getFormName();
            String formKey = item.getFormKey();
            formQueryVO.setSearchName(formName);
            formQueryVO.setSearchKey(formKey);
            // 查询表单是否存在
            boolean valid = checkFormValid(formQueryVO);
            if (!valid) {
                temp.add(item);
                continue;
            }
            // 查询表单是否包含此变量(可能表单存在，但表单内容不一致)
            valid = checkVariableValidInForm(item.getCode(), formQueryVO);
            if (!valid) {
                temp.add(item);
            }
        }
        if (!temp.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (FormVariableVo item : temp) {
                sb.append(item.getName()).append("(").append(item.getCode()).append(")").append(";");
            }
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "部分流程变量无效：" + sb);
        }
        return ResponseResult.success();
    }

    /**
     * 清空所有添加的外置表单下的流程变量
     *
     * @param processModelVo 流程模型
     */
    public void cleanAllVariables(ProcessModelVo processModelVo) {
        // 流程变量存在最外面的properties下
        JSONObject properties = JSON.parseObject(JsonUtils.toJson(processModelVo.getProperties()));
        if (!properties.containsKey("modelVariables")) {
            logger.error("流程变量字段modelVariables缺失");
            return;
        }
        List<FormVariableVo> formVariablesList = JSONObject.parseObject(JsonUtils.toJson(
                properties.getJSONArray("modelVariables")), new TypeReference<List<FormVariableVo>>() {
        });
        if (Objects.isNull(formVariablesList)) {
            logger.error("流程变量字段modelVariables解析失败");
            return;
        }
        // 对每个流程变量, 检查所属的表单
        List<FormVariableVo> temp = new ArrayList<>();
        for (FormVariableVo item : formVariablesList) {
            String source = item.getVariableSource();
            // 来源于表单的变量才要校验
            if ("form".equals(source)) {
                temp.add(item);
            }
        }
        if (!temp.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (FormVariableVo item : temp) {
                sb.append(item.getName()).append("(").append(item.getCode()).append(")").append(";");
            }
            // 删除流程变量
            formVariablesList.removeAll(temp);
            properties.put("modelVariables", formVariablesList);
            processModelVo.setProperties(properties.toJavaObject(Object.class));
        }
        return;
    }

    /**
     * 检查"目标表单"是否在"当前环境+业务id+应用id"下存在
     * !! 20210810：需求转变，内置表单直接清空全部表单(以及表单下变量)。修改内容为checkFormValid函数，注意直接返回true了
     *
     * @param formQueryVO
     * @return true：有效；false：无效
     */
    public boolean checkFormValid(FormQueryVO formQueryVO) {
        PageInfo<FlowFormAppInfoVo> pageInfos = new PageInfo<>(flowFormRepository.list(formQueryVO));
//        logger.info("有效表单："+pageInfos.getList());
        return false;
//        return !pageInfos.getList().isEmpty();
    }

    /**
     * 先查找 目标表单 是否存在，如存在，则继续查找 流程变量 是否在表单中
     *
     * @param variable    流程变量
     * @param formQueryVO 目标表单
     * @return true：存在；false：不存在
     */
    public boolean checkVariableValidInForm(String variable, FormQueryVO formQueryVO) {
        // 查询表单存在（通过key+name，应该最多只会查到一个）
        List<FlowFormAppInfoVo> formList = flowFormRepository.list(formQueryVO);
        if (formList.isEmpty()) {
            return false;
        }
        // 遍历每个表单
        for (FlowFormAppInfoVo form : formList) {
            // 获取表单详情，包含了表单下的流程变量
            FormInfoVO formInfo = flowFormRepository.detail(form.getFormId());
            JSONObject fields = JSONObject.parseObject(JSONObject.toJSONString(formInfo.getFields()));
            JSONObject properties = fields.getJSONObject("properties");
            // key就是流程变量名
            if (properties.containsKey(variable)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查字段下的表单是否有效，无效就清空表单信息
     *
     * @param properties
     * @param appId
     * @param busiId
     * @return 无效的表单集合，name.key形式
     */
    public Set<String> cleanInvalidForm(JSONObject properties, Integer appId, Integer busiId) {
        Set<String> missForms = new HashSet<>();
        boolean valid = true;
        if (!properties.containsKey("formSource") || !"innerForm".equals(properties.get("formSource"))) {
            return missForms;
        }
        if (properties.containsKey("formkeydefinition") && properties.containsKey("formname")) {
            if ("".equals(properties.get("formkeydefinition")) || "".equals(properties.get("formname"))) {
                return missForms;
            }
            String key = properties.getString("formkeydefinition");
            String name = properties.getString("formname");
            FormQueryVO formQueryVO = new FormQueryVO();
            formQueryVO.setAppId(appId);
            formQueryVO.setBusiId(busiId);
            formQueryVO.setSearchName(name);
            formQueryVO.setSearchKey(key);
            valid = checkFormValid(formQueryVO);
            logger.info(String.format("表单[%s-%s]是否有效：%b", name, key, valid));
            if (!valid) {
                missForms.add(name + "." + key);
                // 如果无效，就要清空该部分
                logger.info(String.format("表单[%s-%s]清空", name, key));
                properties.put("formId", "");
                properties.put("formname", "");
                properties.put("formkeydefinition", "");
            }
        }
        return missForms;
    }

    /**
     * 遍历检查表单是否有效，无效就清空某个表单信息
     *
     * @param appId
     * @param busiId
     * @param processModelVo
     * @return 无效的表单集合，name.key形式
     */
    public Set<String> doCheckInvalidFormInput(Integer appId, Integer busiId, ProcessModelVo processModelVo) {
        Set<String> missForms = new HashSet<>();
        Set<String> temp;

        // 检查外部properties启动表单
        JSONObject propertiesObject = JSON.parseObject(JsonUtils.toJson(processModelVo.getProperties()));
        temp = cleanInvalidForm(propertiesObject, appId, busiId);
        if (!temp.isEmpty()) {
            missForms.addAll(temp);
        }
        processModelVo.setProperties(propertiesObject.toJavaObject(Object.class));

        // 检查各个节点properties表单
        List<ModelNodeVo> nodes = processModelVo.getNodes();
        for (ModelNodeVo node : nodes) {
            logger.info("当前节点[校验表单]：label:{}, id:{}, index:{}", node.getLabel(), node.getId(), node.getIndex());
            Object properties = node.getModel().getProperties();
            JSONObject parseObject = JSON.parseObject(JsonUtils.toJson(properties));
            temp = cleanInvalidForm(parseObject, appId, busiId);
            if (!temp.isEmpty()) {
                missForms.addAll(temp);
            }
            node.getModel().setProperties(parseObject.toJavaObject(Object.class));
        }
        processModelVo.setNodes(nodes);

        return missForms;
    }

    /**
     * 判断数据库中模型名称或者模型编码任一存在
     *
     * @param modelKey  模型编码
     * @param modelName 模型名称
     * @return ErrorCode：key存在或name存在；null：都不存在或同属一个model。
     * 如果都存在且分属于不同model，先检查的name，再检查的key
     */
    private ErrorCode checkModelNameOrKeyExists(String modelKey, String modelName) {
        // 根据 流程编码key 查是否已存在
        Model keyModel = repositoryService.createModelQuery().modelKey(modelKey).latestVersion().singleResult();
        // 根据 流程名称name 查是否已存在
        List<Model> nameList = repositoryService.createModelQuery().modelName(modelName).list();
        Model oldModel = repositoryService.createModelQuery().modelKey(modelKey).modelName(modelName).latestVersion().singleResult();
        // 已存在符合key+name都一样的旧模型
        // 需要先联合判断这个，因为key和name可能分属不同的model，查出来都会是true;
        // 而如果是同一个model，则应判定为正常情况;
        if (oldModel != null) {
            return null;
        }
        // name存在
        if (!nameList.isEmpty()) {
            return ErrorCode.MODEL_NAME_EXIST;
        }
        // key存在
        if (keyModel != null) {
            return ErrorCode.MODEL_KEY_EXIST;
        }
        // 都不存在
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map.Entry<ResponseResult, FlowPlatformModelDetailDTO> uploadJson(ModelUploadDTO modelUploadDTO) {
        MultipartFile file = modelUploadDTO.getFile();
        if (file.isEmpty()) {
            return new MapEntry<>(ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "未上传文件"), null);
        }
        // 接收表单内容
        String modelName = modelUploadDTO.getName();
        String modelKey = modelUploadDTO.getKey();
        String description = modelUploadDTO.getDescription();
        Integer appId = modelUploadDTO.getAppId();
        Integer busiId = modelUploadDTO.getBusiId();
        // 读取文件内容
        StringBuilder jsonStr = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
            String tempString = null;
            while ((tempString = reader.readLine()) != null) {
                jsonStr.append(tempString);
            }
        } catch (IOException e) {
            logger.error("文件读取出错", e);
            return new MapEntry<>(ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "文件读取出错"), null);
        }
        // 解析上传的文件
        FlowPlatformModelDetailDTO flowPlatformModelDetailDTO;
        try {
            flowPlatformModelDetailDTO = JSON.parseObject(jsonStr.toString(), FlowPlatformModelDetailDTO.class);
        } catch (JSONException e) {
            logger.error("文件格式错误", e);
            return new MapEntry<>(ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "文件格式错误"), null);
        }
        // 先将外置表单的数据导入到数据库中
        final List<ExternalFormInfoTransferDTO> formInfoTransferDTOS = flowPlatformModelDetailDTO.getFormInfoTransferDTOS();
        if (formInfoTransferDTOS != null && !insertExternalFormBatch(formInfoTransferDTOS)) {
            return new MapEntry<>(ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "外置表单数据导入出错"), null);
        }

        // 表单接收的数据更新过来
        flowPlatformModelDetailDTO.setAppId(appId);
        flowPlatformModelDetailDTO.setKey(modelKey);
        flowPlatformModelDetailDTO.setName(modelName);
        flowPlatformModelDetailDTO.setDescription(description);
        flowPlatformModelDetailDTO.setBusiId(busiId);

        ErrorCode errorCode = checkModelNameOrKeyExists(modelKey, modelName);
        if (errorCode != null) {
            return new MapEntry<>(ResponseResult.fail(
                    errorCode.errCode(),
                    errorCode.errMessage()), null);
        }

        Model oldModel = repositoryService.createModelQuery().modelKey(modelKey).modelName(
                modelName).latestVersion().singleResult();
        String modelId;
        // 新建流程模型
        if (oldModel == null) {
            ModelCreateVo modelCreateVo = new ModelCreateVo();
            modelCreateVo.setName(flowPlatformModelDetailDTO.getName());
            modelCreateVo.setKey(flowPlatformModelDetailDTO.getKey());
            modelCreateVo.setDescription(flowPlatformModelDetailDTO.getDescription());
            modelCreateVo.setAppId(flowPlatformModelDetailDTO.getAppId());
            modelCreateVo.setBusiId(flowPlatformModelDetailDTO.getBusiId());
            Model model = saveModel(modelCreateVo);
            // 保存应用与模型的关联表
            flowModelAppBusiRepository.insert(resolveModelAppBusiEntity(model, modelCreateVo));
            ObjectNode modelNode = JsonUtils.getMapper().createObjectNode();
            modelNode.put("modelId", model.getId());
            // 添加规格操作监控-新增
            newRuleActionAuditLog(model, modelCreateVo, modelNode, "成功");
            // 返回创建成功的modelId
            modelId = model.getId();
            logger.info("新建流程, 新模型id：" + modelId);
        }
        // key+name 都存在，则导入流程模型
        else {
            // 查询这个旧流程的modelId；这个modelId=数据表ACT_REMODEL中的ID_字段。
            modelId = oldModel.getId();
            // 更新流程创建者为当前用户
            if (StringUtils.isNotEmpty(oldModel.getMetaInfo())) {
                FlowModelMetaInfoDTO metaInfoDTO = JsonUtils.toObject(oldModel.getMetaInfo(), FlowModelMetaInfoDTO.class);
                Objects.requireNonNull(metaInfoDTO).setCreatorName(userRepository.getCurrentUser().getUserName());
                oldModel.setMetaInfo(JsonUtils.toJson(metaInfoDTO));
            }
            logger.info("导入流程，旧模型id：" + modelId);
            // 原流程为"设计"状态时，且不插入新流程
            if (oldModel.getCategory().equals(ModelCategoryEnum.DESIGN.getCode())) {
                logger.info("{} 原流程为 设计 状态时，版本号不变: {}", modelId, (oldModel.getVersion()));
//                oldModel.setVersion(oldModel.getVersion() + 1);

                repositoryService.saveModel(oldModel);
                repositoryService.addModelEditorSource(oldModel.getId(), repositoryService.getModelEditorSource(oldModel.getId()));
            }
        }

        // 遍历检查表单是否有效，无效就清空某个表单信息
        ProcessModelVo processModelVo = JsonUtils.toObject(JsonUtils.toJson(
                flowPlatformModelDetailDTO.getModelJson()), ProcessModelVo.class);
        // ***********************************************
        // 20210810：需求转变，内置表单直接清空全部表单(以及表单下变量)。
        // 修改内容为checkFormValid函数，注意直接返回true了;
        // 并且cleanAllVariables中清空了变量；
//        Set<String> missForms = doCheckInvalidFormInput(
//                flowPlatformModelDetailDTO.getAppId(), flowPlatformModelDetailDTO.getBusiId(), Objects.requireNonNull(processModelVo));
        cleanAllVariables(processModelVo);
        // ***********************************************
        // 遍历检查用户是否有效，无效就清空某个用户信息;
//        Set<String> missUsers = doCheckInvalidUserInput(processModelVo);

        processModelVo.setModelId(modelId);
        flowPlatformModelDetailDTO.setModelJson(processModelVo);

        // 更新这个modelId的流程设计
        flowPlatformModelDetailDTO.setModelId(modelId);


        ResponseResult result = ResponseResult.success();
        // 这里可以通过增加一个返回给前端中的字段来避免枚举。
        // 比如单独为请求导入功能增加一个: importErr=[{"errUser"}, {"errForm"}, {...}]
//        if (!missForms.isEmpty() && !missUsers.isEmpty()){
//            result.setErrCode(ErrorCode.MODEL_IMPORT_INVALID_USER_FORM.errCode())
//                    .setErrMsg(
//                            String.format("%s。 无效用户：%s; 无效表单：%s",ErrorCode.MODEL_IMPORT_INVALID_USER_FORM.errMessage()
//                                    , missUsers
//                                    , missForms));
//        }else if (!missForms.isEmpty()) {
//            result.setErrCode(ErrorCode.MODEL_IMPORT_INVALID_FORM.errCode())
//                    .setErrMsg(
//                            String.format("%s。 无效表单：%s",ErrorCode.MODEL_IMPORT_INVALID_FORM.errMessage(), missForms)
//                    );
//        }else if (!missUsers.isEmpty()) {
//            result.setErrCode(ErrorCode.MODEL_IMPORT_INVALID_USER.errCode())
//                    .setErrMsg(
//                            String.format("%s。 无效用户：%s",ErrorCode.MODEL_IMPORT_INVALID_USER.errMessage(), missUsers)
//                    );
//        }

        return new MapEntry<>(result, flowPlatformModelDetailDTO);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult create(ModelCreateVo modelCreateVo) {
        //1.保存模型 TODO 判断保证model的key是唯一的
        // 根据 流程编码key 查是否已存在
        Model keyModel = repositoryService.createModelQuery().modelKey(modelCreateVo.getKey()).latestVersion().singleResult();
        // 根据 流程名称name 查是否已存在
        List<Model> nameList = repositoryService.createModelQuery().modelName(modelCreateVo.getName()).list();
        // 流程名称name 存在
        if (!nameList.isEmpty()) {
            return ResponseResult.fail(ErrorCode.MODEL_NAME_EXIST.errCode(), ErrorCode.MODEL_NAME_EXIST.errMessage());
        }
        // 流程编码key 存在
        else if (Objects.nonNull(keyModel)) {
            return ResponseResult.fail(ErrorCode.MODEL_KEY_EXIST.errCode(), ErrorCode.MODEL_KEY_EXIST.errMessage());
        }
        // 流程编码key 和 流程名称name 都不存在，允许进入新建
        else {
            Model model = saveModel(modelCreateVo);
            //2.保存应用与模型的关联表
            flowModelAppBusiRepository.insert(resolveModelAppBusiEntity(model, modelCreateVo));
            ObjectNode modelNode = JsonUtils.getMapper().createObjectNode();
            modelNode.put("modelId", model.getId());

            //添加规格操作监控-新增
            newRuleActionAuditLog(model, modelCreateVo, modelNode, "成功");

            return ResponseResult.success(modelNode);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult copy(ModelCopyVo modelCopyVo) {
        if (org.apache.commons.lang3.StringUtils.isEmpty(modelCopyVo.getOriginModelId())) {
            return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        if (Objects.isNull(repositoryService.createModelQuery().modelId(modelCopyVo.getOriginModelId()).singleResult())) {
            return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        Model oldModel = repositoryService.createModelQuery().modelKey(modelCopyVo.getKey()).latestVersion().singleResult();
        if (Objects.isNull(oldModel)) {
            Model model = saveModel(modelCopyVo);
            //2.保存应用与模型的关联表
            flowModelAppBusiRepository.insert(resolveModelAppBusiEntity(model, modelCopyVo));
            ObjectNode modelNode = JsonUtils.getMapper().createObjectNode();
            modelNode.put("modelId", model.getId());
            //添加规格操作监控-新增
            copyRuleActionAuditLog(model, modelCopyVo, modelNode, "成功");
            return ResponseResult.success(modelNode);
        } else {
            return ResponseResult.fail(ErrorCode.MODEL_KEY_EXIST.errCode(), ErrorCode.MODEL_KEY_EXIST.errMessage());
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Model update(ModelSaveVo modelSaveVo) throws Exception {
        //TODO 判断编码是否存在
        //流程编码不可修改 20190717
        Model model = repositoryService.createModelQuery().modelKey(modelSaveVo.getKey()).modelId(modelSaveVo.getModelId()).singleResult();
        if (Objects.isNull(model)) {
            logger.info("模型不存在");
            throw new Exception(ErrorCode.NOT_EXIST.errMessage());
        }

        //1.根据当前模型的不同状态，进行不同操作

        //1.1 若为设计状态，则直接更新
        if (Objects.isNull(ModelCategoryEnum.getDescritionByCode(model.getCategory())) || Objects.equals(model.getCategory(), ModelCategoryEnum.DESIGN.getCode())) {
            doUpdateModel(modelSaveVo, model);
            //设计状态下新增规格操作监控记录
            editRuleActionAuditLog(model, ModelCategoryEnum.DESIGN.getDescription(), "", modelSaveVo, "成功");
        } else {
            //1.2 若为历史状态和运行状态，则新增一条数据，原数据不变，新增的数据的版本号修改
            Model latestModel = repositoryService.createModelQuery().modelKey(model.getKey()).latestVersion().singleResult();
            Model newModel = addNewVersionModel(modelSaveVo, latestModel);
            flowModelAppBusiRepository.insert(resolveModelAppBusiEntity(newModel, modelSaveVo));
            //历史和运行状态下会产生两条规格操作监控记录
            //一条编辑
            editRuleActionAuditLog(model, ModelCategoryEnum.getDescritionByCode(model.getCategory()), newModel.getId(), modelSaveVo, "成功");
            // sxf：既然插入了新模型，就要返回新模型
            model = newModel;
        }

        return model;
    }

    @SuppressWarnings("unchecked")
    private Model addNewVersionModel(ModelSaveVo modelSaveVo, Model latestModel) {
        User current = userRepository.getCurrentUser();
        JSONObject modelObjectNode = new JSONObject();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, modelSaveVo.getDescription());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelSaveVo.getName());
        modelObjectNode.put("creatorName", current.getUserName());
        Model newModel = repositoryService.newModel();
        newModel.setVersion(latestModel.getVersion() + 1);

        //模型分类:设计
        newModel.setCategory(ModelCategoryEnum.DESIGN.getCode());
        newModel.setMetaInfo(modelObjectNode.toJSONString());
        newModel.setName(modelSaveVo.getName());
        newModel.setKey(modelSaveVo.getKey());
        String tenantId = resolveTenantId(modelSaveVo.getAppId());
        if (org.apache.commons.lang3.StringUtils.isEmpty(tenantId)) {
            tenantId = resolveTenantIdByAppId(modelSaveVo.getAppId());
        }
        newModel.setTenantId(tenantId);
        repositoryService.saveModel(newModel);

        ProcessModelVo processModel = JsonUtils.toObject(JSONObject.toJSONString(modelSaveVo.getModelJson()), ProcessModelVo.class);
        if (Objects.nonNull(processModel) && Objects.nonNull(processModel.getProperties())) {
            Object properties = processModel.getProperties();
            if (properties instanceof HashMap) {
                ((HashMap) properties).put("process_id", modelSaveVo.getKey());
                ((HashMap) properties).put("name", modelSaveVo.getName());
                ((HashMap) properties).put("documentation", modelSaveVo.getDescription());
            }
            processModel.setModelId(newModel.getId());
        }

        repositoryService.addModelEditorSource(newModel.getId(), JsonUtils.toJsonNotNull(processModel).getBytes(StandardCharsets.UTF_8));
        return newModel;
    }

    /**
     * @param modelSaveVo 对前端传来的数据进行转换后的modelSave，更接近于数据库中的model
     * @param model       查询到的数据库表中的model
     */
    private void doUpdateModel(ModelSaveVo modelSaveVo, Model model) {
        // 更新一遍model的metainfo字段？
        ObjectNode modelJson = (ObjectNode) JsonUtils.readTree(model.getMetaInfo());
        if (Objects.nonNull(modelJson)) {
            if (Objects.isNull(modelJson.get("creatorName"))) {
                modelJson.put("creatorName", model.getCategory());
            }
            modelJson.put("name", modelSaveVo.getName());
            modelJson.put("description", modelSaveVo.getDescription());
            model.setMetaInfo(modelJson.toString());
        }
        // 将传过来的modelSave的字段更新过来
        model.setName(modelSaveVo.getName());
        model.setKey(modelSaveVo.getKey());
        model.setCategory(ModelCategoryEnum.DESIGN.getCode());
        // 保存模型到数据库
        repositoryService.saveModel(model);
        // 保存流程的内容到数据库（入参需为json格式）
        repositoryService.addModelEditorSource(model.getId(), JSONObject.toJSONString(modelSaveVo.getModelJson()).getBytes(StandardCharsets.UTF_8));
        boolean isUpdateAppBusi = false;
        // 根据模型id获取关联app，并更新他的内容
        FlowModelAppBusi modelAppBusiInfo = flowModelAppBusiRepository.getAppByModelId(model.getId());
        if (Objects.isNull(modelAppBusiInfo)) {
            modelAppBusiInfo = new FlowModelAppBusi();
            modelAppBusiInfo.setModelId(model.getId());
            modelAppBusiInfo.setModelName(model.getName());
        }
        if (Objects.nonNull(modelSaveVo.getAppId())) {
            FlowApp appInfo = flowAppRepository.detail(modelSaveVo.getAppId());
            if (Objects.nonNull(appInfo)) {
                isUpdateAppBusi = true;
                modelAppBusiInfo.setAppId(appInfo.getId());
                modelAppBusiInfo.setAppName(appInfo.getAppName());
            }
        }
        if (Objects.nonNull(modelSaveVo.getBusiId())) {
            FlowBusi busiInfo = flowBusiRepository.detail(modelSaveVo.getBusiId());
            if (Objects.nonNull(busiInfo)) {
                isUpdateAppBusi = true;
                modelAppBusiInfo.setBusiId(busiInfo.getId());
                modelAppBusiInfo.setBusiName(busiInfo.getBusiName());
            }
        }
        if (isUpdateAppBusi) {
            if (Objects.isNull(modelAppBusiInfo.getId())) {
                flowModelAppBusiRepository.insert(modelAppBusiInfo);
            } else {
                flowModelAppBusiRepository.update(modelAppBusiInfo);
            }
        }
    }

    public String formParamBackfill(ModelSaveVo modelSaveVo, String id) {
        byte[] modelBytes = repositoryService.getModelEditorSource(id);
        String editorSource = new String(modelBytes, StandardCharsets.UTF_8);
        JsonNode jsonNode = JsonUtils.readTree(editorSource);
        if (jsonNode.get("properties").findValue("formkeydefinition") == null) {
            return JSONObject.toJSONString(modelSaveVo.getModelJson());
        }
        String formkeydefinition = jsonNode.get("properties").findValue("formkeydefinition").asText();
        String formname = jsonNode.get("properties").findValue("formname").asText();
        JsonNode jsonNodeChildShapes = jsonNode.findValue("childShapes");
        JsonNode modelJson1 = JsonUtils.readTree(jsonNodeChildShapes.toString());
        for (int i = 0; i < modelJson1.size(); i++) {
            JsonNode jsonNodeProperties = modelJson1.get(i).findValue("properties");
            JsonNode initiator = jsonNodeProperties.findValue("initiator");
            if (initiator != null) {
                ((ObjectNode) jsonNodeProperties).put("formkeydefinition", formkeydefinition);
                ((ObjectNode) jsonNodeProperties).put("formreference", formname);
            }
            ((ArrayNode) jsonNodeChildShapes).set(i, modelJson1.get(i));
        }
        return jsonNode.toString();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult list(ModelQueryVo modelQueryVo) {
        List<FlowModelQueryInfo> models = new ArrayList<>();
        //若
//        if (catalogueService.checkTenantAuthorize(modelQueryVo)) {
//            return ResponseResult.success(models);
//        }
        PageHelper.startPage(modelQueryVo.getPageNum(), modelQueryVo.getPageSize());
        models = flowModelQueryDAO.selectList(modelQueryVo);
        resolveAttachField(models);
        PageInfo<FlowModelQueryInfo> pageInfo = new PageInfo<>(models);

        ResponseResult responseResult = ResponseResult.success(models);
        responseResult.setTotalCount((int) pageInfo.getTotal());
        responseResult.setPageNum(modelQueryVo.getPageNum());
        responseResult.setPageSize(modelQueryVo.getPageSize());
        return responseResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult listForEntrust(ModelQueryVo modelQueryVo) {
        List<FlowModelQueryInfo> models = new ArrayList<>();
        PageHelper.startPage(modelQueryVo.getPageNum(), modelQueryVo.getPageSize());
        models = flowModelQueryDAO.selectList(modelQueryVo);
        resolveAttachField(models);
        PageInfo<FlowModelQueryInfo> pageInfo = new PageInfo<>(models);
        ResponseResult responseResult = ResponseResult.success(models);
        responseResult.setTotalCount((int) pageInfo.getTotal());
        responseResult.setPageNum(modelQueryVo.getPageNum());
        responseResult.setPageSize(modelQueryVo.getPageSize());
        return responseResult;
    }

    @Override
    public ResponseResult<List<ModelVersionDetailDTO>> getModelVersionDetail(ModelQueryVo modelQueryVo) {
        if (StringUtils.isBlank(modelQueryVo.getSearchKey())) {
            return ResponseResult.fail(ErrorCode.PARAM_NOT_EXIST.errCode(), ErrorCode.PARAM_NOT_EXIST.errMessage());
        }
        PageHelper.startPage(modelQueryVo.getPageNum(), modelQueryVo.getPageSize());
        List<ModelVersionDetail> modelVersionDetailList = flowModelQueryDAO.selectModelVersionDetail(modelQueryVo.getSearchKey());
        List<ModelVersionDetailDTO> modelVersionDetailDTOList = new ArrayList<>(modelVersionDetailList.size());
        if (Objects.nonNull(modelVersionDetailList) && !modelVersionDetailList.isEmpty()) {
            modelVersionDetailList.stream().forEach(modelVersionDetail -> {
                ModelVersionDetailDTO modelVersionDetailDTO = new ModelVersionDetailDTO();
                BeanCopierUtils.copyProperties(modelVersionDetail, modelVersionDetailDTO);
                modelVersionDetailDTOList.add(modelVersionDetailDTO);
            });
        }
        PageInfo<ModelVersionDetail> pageInfo = new PageInfo<>(modelVersionDetailList);

        ResponseResult responseResult = ResponseResult.success(modelVersionDetailDTOList);
        responseResult.setTotalCount((int) pageInfo.getTotal());
        responseResult.setPageNum(modelQueryVo.getPageNum());
        responseResult.setPageSize(modelQueryVo.getPageSize());
        return responseResult;
    }


    private void resolveAttachField(List<FlowModelQueryInfo> models) {
        for (FlowModelQueryInfo flowModelQueryInfo : models) {
            Map<String, Object> map = JSONObject.parseObject(flowModelQueryInfo.getMetaInfo(), new TypeReference<Map<String, Object>>() {
            });
            if (Objects.nonNull(map)) {
                flowModelQueryInfo.setDescription((String) map.get("description"));
                flowModelQueryInfo.setCreatorName((String) map.get("creatorName"));
                //兼容旧数据
                if (org.apache.commons.lang3.StringUtils.isBlank(flowModelQueryInfo.getCreatorName())) {
                    flowModelQueryInfo.setCreatorName(flowModelQueryInfo.getCategory());
                }
            }
            //默认为设计状态
            if (Objects.isNull(ModelCategoryEnum.getDescritionByCode(flowModelQueryInfo.getCategory()))) {
                flowModelQueryInfo.setCategory(ModelCategoryEnum.DESIGN.getCode());
            }

            flowModelQueryInfo.setCategoryDesc(ModelCategoryEnum.getDescritionByCode(flowModelQueryInfo.getCategory()));

        }
    }

    @Override
    public List<FlowProcdefQueryInfo> deploymentList(ModelQueryVo modelQueryVo) {
        if (catalogueService.checkTenantAuthorize(modelQueryVo)) {
            return Collections.emptyList();
        }
        PageHelper.startPage(modelQueryVo.getPageNum(), modelQueryVo.getPageSize());
        List<FlowProcdefQueryInfo> flowProcdefQueryInfos = flowProcdefQueryDAO.selectDeploymentListOfRunningModel(modelQueryVo);
        for (FlowProcdefQueryInfo entity : flowProcdefQueryInfos) {
            Map<String, Object> map = JSONObject.parseObject(entity.getMetaInfo(), new TypeReference<Map<String, Object>>() {
            });
            if (Objects.nonNull(map.get("creatorName"))) {
                entity.setCreatorName((String) map.get("creatorName"));
            }
        }
        return flowProcdefQueryInfos;
    }

    @Override
    public List<ModelSimpleVo> listModel(ModelQueryVo modelQueryVo) {
        List<FlowProcdefQueryInfo> flowProcdefQueryInfos = flowProcdefQueryDAO.selectDeploymentList(modelQueryVo);
        return flowProcdefQueryInfos.stream().map(item -> {
            ModelSimpleVo tmp = new ModelSimpleVo();
            tmp.setModelKey(item.getProcDefKey());
            tmp.setModelName(item.getProcDefName());
            return tmp;
        }).collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Map<String, Object>> getCandidateGroups(Integer appId) {
        if (Objects.isNull(appId)) {
            return getDefaultCandidateGroup();
        }
        FlowApp appInfo = flowAppRepository.detail(appId);
        if (Objects.isNull(appInfo)) {
            logger.error("---获取应用{}角色列表失败---", appId);
            return new ArrayList<>();
        }

        FlowThirdPlatformUrl thirdPlatformUrlInfo = flowThirdPlatformUrlRepository.get(appInfo.getAppCode(), ThirdPlatformUrlEnum.ROLE_QUERY.type());
        if (Objects.isNull(thirdPlatformUrlInfo)) {
            return getDefaultCandidateGroup();
        } else {
            return getCandidateGroupFromOtherPlatform(thirdPlatformUrlInfo);
        }
    }

    private List<Map<String, Object>> getCandidateGroupFromOtherPlatform(FlowThirdPlatformUrl thirdPlatformUrlInfo) {
        List<Map<String, Object>> candidateGroups = new ArrayList<>();
        JSONObject jsonParam = new JSONObject();
        jsonParam.put("COMMAND_CODE", "qryAppRoles");
        jsonParam.put("SERIAL", "");
        jsonParam.put("searchName", "");

        Map<String, String> headMap = new HashMap<>(2);
        headMap.put("Content-Type", "application/json");

        String roles = HttpClientUtils.httpPost(thirdPlatformUrlInfo.getUrl(), jsonParam.toString(), headMap);
        try {
            JSONObject roleObject = JSON.parseObject(roles);
            if (Objects.nonNull(roleObject)) {
                String message = roleObject.getString("errMsg");
                String sucessStr = "success";
                if (sucessStr.equalsIgnoreCase(message)) {
                    JSONArray array = roleObject.getJSONArray("data");
                    for (Object role : array) {
                        String roleName = ((JSONObject) role).getString("name");
                        String roleCode = ((JSONObject) role).getString("roleCode");
                        Map<String, Object> candidateMap = new HashMap<>(2);
                        candidateMap.put("roleName", roleName);
                        candidateMap.put("roleCode", roleCode);
                        candidateGroups.add(candidateMap);
                    }
                }
            }
        } catch (Exception e) {
            logger.info("------{}:获取角色列表失败:{}", thirdPlatformUrlInfo.getUrl(), e.getMessage());
        }

        return candidateGroups;
    }

    @SuppressWarnings("unchecked")
    private List<Map<String, Object>> getDefaultCandidateGroup() {
        List<Map<String, Object>> candidateGroups = new ArrayList<>();
        Map<String, Object> candidateMap = new HashMap<>(2);
        candidateGroups.add(candidateMap);
        /*List<Map<String, Object>> roleList = (List<Map<String, Object>>) fourACommonAdapter.getRoleByAppCode().getData();
        for (Map<String, Object> roleMap : roleList) {

            candidateMap.put("roleName", roleMap.get("ROLE_NAME"));
            candidateMap.put("roleCode", roleMap.get("ROLE_ID"));
            candidateGroups.add(candidateMap);
        }*/
        return candidateGroups;
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public FlowPlatformModelDetailDTO getModelDetail(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if (model == null) {
            return null;
        }
        return getModelDetail(model.getId(), model);
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public FlowPlatformModelDetailDTO getModelDetail(String modelId, String modelCode) {
        Model model = null;
        if (StringUtils.isNotEmpty(modelId)) {
            model = repositoryService.getModel(modelId);
        }
        if (StringUtils.isNotEmpty(modelCode)) {
            List<FlowModelQueryInfo> flowModelQueryInfos = flowModelQueryDAO.selectModel(modelCode);
            if (flowModelQueryInfos != null && flowModelQueryInfos.size() > 0) {
                FlowModelQueryInfo flowModelQueryInfo = flowModelQueryInfos.get(0);
                modelId = flowModelQueryInfo.getModelId();
                model = new ModelEntityImpl();
                model.setKey(flowModelQueryInfo.getModelKey());
                model.setName(flowModelQueryInfo.getModelName());
                model.setMetaInfo(flowModelQueryInfo.getMetaInfo());
            }
        }
        if (model == null) {
            return null;
        }
        return getModelDetail(modelId, model);
    }

    private FlowPlatformModelDetailDTO getModelDetail(String modelId, Model model) throws IOException {
        FlowPlatformModelDetailDTO modelDetailDTO = null;
        if (StringUtils.isNotEmpty(model.getMetaInfo())) {
            modelDetailDTO = JsonUtils.toObject(model.getMetaInfo(), FlowPlatformModelDetailDTO.class);
        }
        if (Objects.isNull(modelDetailDTO)) {
            modelDetailDTO = new FlowPlatformModelDetailDTO();
        }
        modelDetailDTO.setKey(model.getKey());
        modelDetailDTO.setName(model.getName());
        modelDetailDTO.setModelId(modelId);
        modelDetailDTO.setVersion(model.getVersion());
        String editorSource = new String(repositoryService.getModelEditorSource(modelId), StandardCharsets.UTF_8);
        ProcessModelVo processModel = JsonUtils.toObject(editorSource, ProcessModelVo.class);
        if (processModel != null) {
            modelDetailDTO.setModelJson(
                    JsonUtils.readTree(JsonUtils.toJsonNotNull(
                            ModelUtils.getInstance().transformToViewModel(processModel))));
        }
        FlowModelAppBusi modelAppBusiInfo = flowModelAppBusiRepository.getAppByModelId(modelId);
        if (Objects.nonNull(modelAppBusiInfo)) {
            modelDetailDTO.setAppId(modelAppBusiInfo.getAppId());
            modelDetailDTO.setBusiId(modelAppBusiInfo.getBusiId());
        }
        return modelDetailDTO;
    }

    @Override
    public ObjectNode getDeploymentDetail(String deploymentId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        if (Objects.isNull(processDefinition)) {
            return null;
        }
        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName() + ".json");
        ProcessModelVo processModel = ModelUtils.getInstance().streamToModel(inputStream);
        ObjectNode objectNode = JsonUtils.getMapper().createObjectNode();
        objectNode.put("name", processDefinition.getName());
        objectNode.put("key", processDefinition.getKey());
        objectNode.put("description", processDefinition.getDescription());
        objectNode.put("revision", processDefinition.getVersion());
        if (processModel != null) {
            objectNode.put("modelId", processModel.getModelId());
            FlowModelAppBusi modelAppBusiInfo = flowModelAppBusiRepository.getAppByModelId(processModel.getModelId());
            if (Objects.nonNull(modelAppBusiInfo)) {
                objectNode.put("appId", modelAppBusiInfo.getAppId());
                objectNode.put("busiId", modelAppBusiInfo.getBusiId());
            }
            objectNode.set("modelJson", JsonUtils.readTree(JsonUtils.toJsonNotNull(ModelUtils.getInstance().transformToViewModel(processModel))));
        } else {
            objectNode.set("modelJson", JsonUtils.getMapper().createObjectNode());
        }
        return objectNode;
    }

    /**
     * 使用EventConfigPropertiesProcessor处理"开始"和"结束"节点，将eventConfig插入数据库
     *
     * @param nodeList
     * @param modelNode
     */
    private void executeEventConfigOfStartEndProcessor(List<JsonNode> nodeList, JsonNode modelNode) {
        for (JsonNode elementNode : nodeList) {
            final EventConfigPropertiesProcessor processor = SpringUtils.getBean(EventConfigPropertiesProcessor.class);
            if (processor.support(elementNode)) {
                try {
                    processor.process(FlowElementProcessContext.builder()
                            .deployId(DeployIdContainer.getDeployId())
                            .nodeCode(elementNode.get("properties").get("overrideid").asText())
                            .modelNode(modelNode)
                            .elementNode(elementNode)
                            .build());
                } catch (Exception e) {
                    logger.info("executeStartEndPropertiesProcessor Error,Reason:" + e.getLocalizedMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 解析出"开始"和"结束"节点
     *
     * @param modelNode 整个模型节点
     * @return 开始+结束 节点
     */
    public List<JsonNode> processStartEndNode(JsonNode modelNode) {
        List<JsonNode> nodeList = new ArrayList<>();
        if (modelNode != null && modelNode.get("childShapes") != null) {
            Iterator childShapes = modelNode.get("childShapes").iterator();
            JsonNode jsonChildNode;
            String stencilId;
            while (childShapes.hasNext()) {
                jsonChildNode = (JsonNode) childShapes.next();
                stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                if (("StartNoneEvent".equals(stencilId) || "EndNoneEvent".equals(stencilId))) {
                    nodeList.add(jsonChildNode);
                }
            }
        }
        return nodeList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Deployment deploy(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if (model == null) {
            return null;
        }
        byte[] modelBytes = repositoryService.getModelEditorSource(model.getId());
        logger.info("ModelId:" + model.getId() + " ModelBytes:");
        String editorSource = new String(modelBytes, StandardCharsets.UTF_8);
        logger.info(editorSource);
        JsonNode modelJson = JsonUtils.readTree(editorSource);
        CustomBpmnJsonConverter bpmnJsonConverter = new CustomBpmnJsonConverter();
        CustomUserTaskJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        CustomScriptTaskJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        CustomServiceTaskJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        CustomSubProcessJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        String deployId = idGenerator.getNextId();
        DeployIdContainer.setDeployId(deployId);
        try {
            // 处理 UserTask节点
            BpmnModel bpmnModel = bpmnJsonConverter.convertToBpmnModel(modelJson);
            // 处理 开始、结束节点
            List<JsonNode> nodeList = processStartEndNode(modelJson);
            executeEventConfigOfStartEndProcessor(nodeList, modelJson);
            if (CollectionUtils.isEmpty(bpmnModel.getProcesses())) {
                logger.warn("未绘制流程图");
            }
            String processName = model.getName() + ".bpmn20.xml";
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().name(model.getName()).addBpmnModel(processName, bpmnModel);
            ((DeploymentBuilderImpl) deploymentBuilder).getDeployment().setId(deployId);
            //将表单资源添加到deploy中，后续自动部署资源
            //resolveDeploymentResources(deploymentBuilder, resolveModelFormKeys(bpmnModel));
            Deployment deployment = deploymentBuilder.deploy();
            //部署操作监控记录
            deployRuleActionAuditLog(model, ModelCategoryEnum.getDescritionByCode(model.getCategory()), modelId, deployment, "成功");
            if (Objects.nonNull(deployment)) {
                ModelDeploymentContext context = ModelDeploymentContext.builder()
                        .deployId(deployId)
                        .modelJson(modelJson)
                        .modelId(modelId)
                        .processName(processName)
                        .modelBytes(modelBytes)
                        .model(model)
                        .bpmnModel(bpmnModel)
                        .build();
                for (ModelDeploymentPostProcessor modelDeploymentPostProcessor : modelDeploymentPostProcessors) {
                    try {
                        modelDeploymentPostProcessor.process(context);
                    } catch (Exception e) {
                        logger.info("ModelDeploymentPostProcessor Process Error, Reason:" + e.getLocalizedMessage());
                        e.printStackTrace();
                    }
                }
                return deployment;
            }
        } finally {
            DeployIdContainer.remove();
        }
        return null;
    }

    @Override
    public JsonNode getModelVariables(String modelId) {
        byte[] modelEditorSource = repositoryService.getModelEditorSource(modelId);
        String editorSource = null;
        if (modelEditorSource != null && modelEditorSource.length > 0) {
            editorSource = new String(modelEditorSource, StandardCharsets.UTF_8);
        }
        if (StringUtils.isNotEmpty(editorSource)) {
            ProcessModelVo processModel = JsonUtils.toObject(editorSource, ProcessModelVo.class);
            if (processModel != null) {
                try {
                    JsonNode modelJson = JsonUtils.readTree(JsonUtils.toJsonNotNull(ModelUtils.getInstance().transformToViewModel(processModel)));
                    JsonNode properties = modelJson.get("properties");
                    JsonNode modelVariables = properties.get("modelVariables");
                    return modelVariables;
                } catch (Exception e) {
                }
            }
        }
        return null;
    }

    @Override
    public boolean deleteModel(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if (isDesignCategory(model)) {
            //设计状态下可直接删除
            repositoryService.deleteModel(modelId);
            //设计状态下删除规格操作监控记录
            deleteRuleActionAuditLog(model, ModelCategoryEnum.DESIGN.getDescription(), modelId, true, "成功");
            return true;
        }

        //历史，运行状态下，判断是否创建过实例，若没有，则不允许删除
        List<FlowModelProcdef> modelProcDefs = flowModelProcdefRepository.getModelProcDefByModelId(model.getId());
        if (CollectionUtils.isEmpty(modelProcDefs)) {
            repositoryService.deleteModel(modelId);
            //历史，运行状态下删除规格操作监控记录
            deleteRuleActionAuditLog(model, ModelCategoryEnum.getDescritionByCode(model.getCategory()), modelId, true, "成功");
            return true;
        }

        Set<String> processDefinitionIds = modelProcDefs.stream().map(FlowModelProcdef::getProcDefId).collect(Collectors.toSet());
        ProcessInstanceStatisticsQueryVo queryVo = new ProcessInstanceStatisticsQueryVo();
        queryVo.setProcessDefinitionIds(processDefinitionIds);
        List<FlowProcessInstanceTraceVo> processInstanceTraces = processInstanceTraceService.getProcessInstanceTraces(queryVo);
        if (CollectionUtils.isEmpty(processInstanceTraces)) {
            repositoryService.deleteModel(modelId);
            //历史，运行状态下删除规格操作监控记录
            deleteRuleActionAuditLog(model, ModelCategoryEnum.getDescritionByCode(model.getCategory()), modelId, true, "成功");
            return true;
        }
        return false;
    }


    @Override
    public boolean offlineModel(String modelId) {
        Model model = repositoryService.getModel(modelId);
        // 在线 状态下可直接停用
        if (isRunningCategory(model)) {
            model.setCategory(ModelCategoryEnum.HISTORY.getCode());
            repositoryService.saveModel(model);

            // 添加停用操作监控记录
            offlineRuleActionAuditLog(model, ModelCategoryEnum.HISTORY.getDescription(), modelId, true, "成功");
            return true;
        }
        return false;
    }

    /**
     * @param modelId
     * @return
     * @Description 检查强制选人第一节点和后续节点规则校验
     * @author liujinjian
     * @date 2021-11-24
     */
    @Override
    public ResponseResult checkFirstNodeForcePickByForm(String modelId) {
        // 根据模型id获取模型具体内容
        Model model = repositoryService.getModel(modelId);
        if (model == null) {
            logger.warn("流程模型不存在");
            return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        byte[] modelBytes = repositoryService.getModelEditorSource(model.getId());
        String editorSource = new String(modelBytes, StandardCharsets.UTF_8);
        // 定义强制选人Map，节点处理人Map,节点处理人已选中的处理人Map变量
        Map<String, String> forcePickMap = new HashMap<>();
        Map<String, String> nodeHandlerMap = new HashMap<>();
        Map<String, String> pickHandlerMap = new HashMap<>();
        Map<String, String> firstNodeHandlerMap = new HashMap<>();
        JsonNode modelJson = JsonUtils.readTree(editorSource);
        // 处理 UserTask节点
        JsonNode jsonChildNode = null, outgoingNode = null;
        String stencilId = "", resourceId = "", nodeCode = "";
        boolean firstNodeSetForcePick = false;
        if (modelJson != null && modelJson.get("childShapes") != null) {
//            ((ObjectNode) modelJson)._children.get("childShapes").get(0).get("outgoing").get(0).get("resourceId").textValue();
            Iterator childShapes = modelJson.get("childShapes").iterator();
            // 获取开始节点
            while (childShapes.hasNext()) {
                jsonChildNode = (JsonNode) childShapes.next();
                stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                if ("StartNoneEvent".equals(stencilId)) {
                    outgoingNode = jsonChildNode.get("outgoing");
                    break;
                }
            }
            // 获取开始节点下一节点
            JsonNode jsonData = modelJson.get("childShapes");
            if (outgoingNode.size() > 0) {
                for (int i = 0; i < outgoingNode.size(); i++) {
                    JsonNode objNode = outgoingNode.get(i);
                    if (!objNode.isNull()) {
                        resourceId = objNode.get("resourceId").textValue();
                        for (int j = 0; j < jsonData.size(); j++) {
                            jsonChildNode = jsonData.get(j);
                            stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                            if (resourceId.equalsIgnoreCase(jsonChildNode.get("resourceId").textValue())) {
                                // 第一节点数据处理开始
                                JsonNode tempJsonNode = jsonChildNode.get("outgoing");
                                for (int k = 0; k < tempJsonNode.size(); k++) {
                                    JsonNode objNodeTemp = tempJsonNode.get(k);
                                    if (!objNodeTemp.isNull()) {
                                        resourceId = objNodeTemp.get("resourceId").textValue();
                                        for (int h = 0; h < jsonData.size(); h++) {
                                            jsonChildNode = jsonData.get(h);
                                            stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                                            if (resourceId.equalsIgnoreCase(jsonChildNode.get("resourceId").textValue())) {
                                                if ("UserTask".equals(stencilId)) {
                                                    if (jsonChildNode.get("properties").has("forcePick")) {
                                                        firstNodeHandlerMap.put(jsonChildNode.get("properties").get("overrideid").textValue(), jsonChildNode.get("properties").get("forcePick").textValue());
                                                    } else {
                                                        firstNodeHandlerMap.put(jsonChildNode.get("properties").get("overrideid").textValue(), "0");
                                                    }
                                                }
                                                // 并行网关
                                                if ("ParallelGateway".equalsIgnoreCase(stencilId)) {
                                                    setFirstNodeHandlerMapVal(firstNodeHandlerMap, jsonChildNode, jsonData);
                                                }
                                                // 排他网关
                                                if ("ExclusiveGateway".equalsIgnoreCase(stencilId)) {
                                                    setFirstNodeHandlerMapVal(firstNodeHandlerMap, jsonChildNode, jsonData);
                                                }
                                                // 包含网关
                                                if ("InclusiveGateway".equalsIgnoreCase(stencilId)) {
                                                    setFirstNodeHandlerMapVal(firstNodeHandlerMap, jsonChildNode, jsonData);
                                                }
                                                // 子流程
                                                if ("SubProcess".equalsIgnoreCase(stencilId)) {
                                                    JsonNode subFlowChildNode = jsonChildNode.get("childShapes");
                                                    // 查找子流程开始节点
                                                    for (int cc = 0; cc < subFlowChildNode.size(); cc++) {
                                                        jsonChildNode = subFlowChildNode.get(cc);
                                                        if (!jsonChildNode.isNull()) {
                                                            stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                                                            if ("StartNoneEvent".equals(stencilId)) {
                                                                outgoingNode = jsonChildNode.get("outgoing");
                                                                break;
                                                            }
                                                        }
                                                    }
                                                    setFirstNodeHandlerMapVal(firstNodeHandlerMap, jsonChildNode, subFlowChildNode);
                                                }
                                            }
                                        }
                                    }
                                }
                                // 第一节点数据处理结束
                            }
                        }
                    }
                }
            }
            for (String key : firstNodeHandlerMap.keySet()) {
                if ("1".equalsIgnoreCase(firstNodeHandlerMap.get(key))) {
                    firstNodeSetForcePick = true;
                    nodeCode = key;
                    break;
                }
            }
            if (firstNodeSetForcePick) {
                return ResponseResult.fail(ErrorCode.FIRST_NODE_NO_SETTING_FORCE_PICK.errCode(), ErrorCode.FIRST_NODE_NO_SETTING_FORCE_PICK.errMessage() + ";节点编码[" + nodeCode + "]");
            }
            // 还有强制选人的节点没有被处理到--业务逻辑
            for (int i = 0; i < jsonData.size(); i++) {
                jsonChildNode = jsonData.get(i);
                stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                if ("UserTask".equalsIgnoreCase(stencilId)) {
                    processUserTaskData(forcePickMap, nodeHandlerMap, pickHandlerMap, jsonChildNode);
                }
                if ("SubProcess".equalsIgnoreCase(stencilId)) {
                    JsonNode subFlowChildNode = jsonChildNode.get("childShapes");
                    for (int j = 0; j < subFlowChildNode.size(); j++) {
                        jsonChildNode = subFlowChildNode.get(j);
                        stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                        if ("UserTask".equals(stencilId)) {
                            processUserTaskData(forcePickMap, nodeHandlerMap, pickHandlerMap, jsonChildNode);
                        }
                    }
                }
            }
        }
        // 判断强制选人节点是否被配置上
        if (forcePickMap != null && !forcePickMap.isEmpty()) {
            for (Map.Entry<String, String> entry : forcePickMap.entrySet()) {
                if (StringUtils.isEmpty(pickHandlerMap.get(entry.getKey()))) {
                    return ResponseResult.fail(ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errCode(), ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errMessage() + ",用户任务节点编码:[" + entry.getKey() + "]");
                }
            }
        }
        // 判断开启了指定下一节点处理人按钮是否配置了强制选人节点且强制选人节点都被配置分配了
        if (nodeHandlerMap != null && !nodeHandlerMap.isEmpty()) {
            String handlerNodeCodeStr = ";指定流程节点处理人节点编码:[";
            for (Map.Entry<String, String> entry : nodeHandlerMap.entrySet()) {
                handlerNodeCodeStr += entry.getKey() + ",";
            }
            handlerNodeCodeStr = handlerNodeCodeStr.substring(0, handlerNodeCodeStr.length() - 1) + "]";
            // 开启了指定节点处理人功能但是未配置强制选人节点
            if (pickHandlerMap.isEmpty()) {
                return ResponseResult.fail(ErrorCode.NODE_CANDIDATE_NO_PICKED.errCode(), ErrorCode.NODE_CANDIDATE_NO_PICKED.errMessage() + handlerNodeCodeStr);
            }
            if (forcePickMap.isEmpty()) {
                return ResponseResult.fail(ErrorCode.NO_SETTING_FORCE_PICK_PROCESSED.errCode(), ErrorCode.NO_SETTING_FORCE_PICK_PROCESSED.errMessage() + handlerNodeCodeStr);
            }
        }

        return ResponseResult.success();
    }

    /**
     * @param modelId
     * @return
     * @Description 判断第一个节点处理人留空
     * @author lzb
     * @date 2021-12-14
     */
    @Override
    public ResponseResult checkFirstNodeCandidateBlank(String modelId) {
        boolean isCandidateBlank = true;
        Model model = repositoryService.getModel(modelId);
        if (model == null) {
            logger.warn("流程模型不存在");
            return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        byte[] modelBytes = repositoryService.getModelEditorSource(model.getId());
        String editorSource = new String(modelBytes, StandardCharsets.UTF_8);
        JsonNode modelJson = JsonUtils.readTree(editorSource);
        if (Objects.nonNull(modelJson) && Objects.nonNull(modelJson.get("childShapes"))) {
            JsonNode jsonChildNode = null;
            Iterator childShapes = modelJson.get("childShapes").iterator();
            String startResourceId = "", stencilId = "";
            //获取开始节点
            while (childShapes.hasNext()) {
                jsonChildNode = (JsonNode) childShapes.next();
                stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                if ("StartNoneEvent".equals(stencilId) && Objects.nonNull(jsonChildNode.get("outgoing"))) {
                    startResourceId = jsonChildNode.get("outgoing").get(0).get("resourceId").textValue();
                    break;
                }
            }
            //获取第一个任务节点
            JsonNode firstUserTaskNode = getFirstUserTaskNode(modelJson.get("childShapes"), startResourceId);
            if (Objects.nonNull(firstUserTaskNode)) {
                JsonNode propertiesNode = firstUserTaskNode.get("properties");
                if (Objects.nonNull(propertiesNode) && Objects.nonNull(propertiesNode.get("candidateSetNull"))) {
                    //判断第一个任务节点 办理人留空
                    String candidateValue = propertiesNode.get("candidateSetNull").textValue();
                    if (candidateValue.equals("1")) {
                        isCandidateBlank = false;
                    }
                }
            }
        }
        if (!isCandidateBlank) {
            return ResponseResult.fail(ErrorCode.FILE_NODE_CANDIDATE_BLANK_FAIL.errCode(), ErrorCode.FILE_NODE_CANDIDATE_BLANK_FAIL.errMessage());
        } else {
            return ResponseResult.success();
        }
    }

    @Override
    public ResponseResult getModelList(ModelQueryNoPageVo modelQueryNoPageVo) {
        // 默认查询正在运行的流程模型
        modelQueryNoPageVo.setCategory("RUNNING");
        // 取最新版本
        modelQueryNoPageVo.setLastVersion(1);
        // 查询所有应用下的流程模型列表
        List<FlowModelQueryInfo> models = flowModelQueryDAO.getModelList(modelQueryNoPageVo);
        if (CollectionUtils.isEmpty(models)) {
            return ResponseResult.success(new ArrayList<>());
        }

        // 返回数据列表
        ResponseResult responseResult = ResponseResult.success(models);
        responseResult.setTotalCount(models.size());
        return responseResult;
    }

    @Override
    public boolean checkModelExist(String modelName, String modelKey) {
        Model model = repositoryService.createModelQuery().modelKey(modelKey).modelName(modelName).latestVersion().singleResult();
        return model != null;
    }


    /**
     * 根据outgoing->resourceId查找第一个任务节点
     *
     * @param childShapesNode
     * @param resourceId
     * @return
     */
    private JsonNode getFirstUserTaskNode(JsonNode childShapesNode, String resourceId) {
        for (JsonNode node : childShapesNode) {
            if (Objects.equals(node.get("resourceId").textValue(), resourceId)) {
                //判断节点是任务节点
                if (Objects.equals(BpmnJsonConverterUtil.getStencilId(node), "UserTask")) {
                    return node;
                } else {
                    if (Objects.nonNull(node.get("outgoing")) && Objects.nonNull(node.get("outgoing").get(0))) {
                        JsonNode firstUserTaskNode = getFirstUserTaskNode(childShapesNode, node.get("outgoing").get(0).get("resourceId").textValue());
                        if (Objects.nonNull(firstUserTaskNode)) {
                            return firstUserTaskNode;
                        }
                    }
                }
            }
        }
        return null;
    }


    /**
     * 设置流程第一用户节点是否强制选人Fun
     *
     * @param firstNodeHandlerMap
     * @param jsonChildNode
     * @param jsonData
     */
    private void setFirstNodeHandlerMapVal(Map<String, String> firstNodeHandlerMap, JsonNode jsonChildNode, JsonNode jsonData) {
        JsonNode tempNodeList = jsonChildNode.get("outgoing");
        for (int kk = 0; kk < tempNodeList.size(); kk++) {
            JsonNode tempNode = tempNodeList.get(kk);
            if (!tempNode.isNull()) {
                String tempResourceId = tempNode.get("resourceId").textValue();
                for (int hh = 0; hh < jsonData.size(); hh++) {
                    JsonNode jsonTempNode = jsonData.get(hh);
                    String stencilId = BpmnJsonConverterUtil.getStencilId(jsonTempNode);
                    if (tempResourceId.equalsIgnoreCase(jsonTempNode.get("resourceId").textValue())) {
                        tempNode = jsonTempNode.get("outgoing");
                        if (!"UserTask".equals(stencilId)) {
                            if (!tempNode.isNull()) {
                                for (int jj = 0; jj < tempNode.size(); jj++) {
                                    tempResourceId = tempNode.get(jj).get("resourceId").textValue();
                                    for (int ll = 0; ll < jsonData.size(); ll++) {
                                        jsonTempNode = jsonData.get(ll);
                                        stencilId = BpmnJsonConverterUtil.getStencilId(jsonTempNode);
                                        if (tempResourceId.equalsIgnoreCase(jsonTempNode.get("resourceId").textValue())) {
                                            if ("UserTask".equals(stencilId)) {
                                                if (jsonTempNode.get("properties").has("forcePick")) {
                                                    firstNodeHandlerMap.put(jsonTempNode.get("properties").get("overrideid").textValue(), jsonTempNode.get("properties").get("forcePick").textValue());
                                                } else {
                                                    firstNodeHandlerMap.put(jsonTempNode.get("properties").get("overrideid").textValue(), "0");
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 处理节点用户任务候选人数据
     *
     * @param forcePickMap
     * @param nodeHandlerMap
     * @param pickHandlerMap
     * @param jsonChildNode
     */
    private void processUserTaskData(Map<String, String> forcePickMap, Map<String, String> nodeHandlerMap, Map<String, String> pickHandlerMap, JsonNode jsonChildNode) {
        boolean isNextNodeHandler;
        String forcePick;
        String key;
        String controlNode;
        JsonNode nextNodeHandler;
        String val;
        if (jsonChildNode.get("properties").has("forcePick")) {
            forcePick = jsonChildNode.get("properties").get("forcePick").textValue();
        } else {
            forcePick = "0";
        }
        nextNodeHandler = jsonChildNode.get("properties").get("nodeAuth").get("nextNodeHandler");
        if (Objects.isNull(nextNodeHandler)) {
            isNextNodeHandler = false;
        } else {
            isNextNodeHandler = nextNodeHandler.asBoolean();
        }
        key = jsonChildNode.get("properties").get("overrideid").textValue();
        val = jsonChildNode.get("properties").get("name").textValue();
        if ("1".equalsIgnoreCase(forcePick)) {
            forcePickMap.put(key, val);
        }
        if (isNextNodeHandler) {
            nodeHandlerMap.put(key, val);
            if (Objects.nonNull(jsonChildNode.get("properties").get("controlNode"))) {
                Iterator<JsonNode> cnode = jsonChildNode.get("properties").get("controlNode").iterator();
                while (cnode.hasNext()) {
                    controlNode = cnode.next().textValue();
                    pickHandlerMap.put(controlNode, controlNode);
                }
            }
        }
    }

    /**
     * 根据资源ID和流程节点获取第一个用户任务实例对象
     *
     * @param resourceId
     * @param modelJson
     * @param jsonChildNode
     * @return
     */
    private JsonNode getUserTaskFirstNodeData(String resourceId, JsonNode modelJson, JsonNode jsonChildNode) {
        Iterator childShapes = modelJson.get("childShapes").iterator();
        while (childShapes.hasNext()) {
            JsonNode childNode = (JsonNode) childShapes.next();
            if (resourceId.equalsIgnoreCase(childNode.get("resourceId").textValue())) {
                if (childNode.get("childShapes") != null && childNode.get("childShapes").size() > 0) {
                    jsonChildNode = childNode.get("childShapes").get(1);
                    break;
                } else {
                    if ("UserTask".equalsIgnoreCase(childNode.get("node").get("shape").textValue())) {
                        logger.info("=======" + childNode.asText());
                    } else {
                        Iterator outgoings = childNode.get("outgoing").iterator();
                        while (outgoings.hasNext()) {
                            Object obj = outgoings.next();
                            resourceId = ((ObjectNode) obj).get("resourceId").textValue();
                            getUserTaskFirstNodeData(resourceId, modelJson, jsonChildNode);
                        }
                    }
                }
            }
        }
        return jsonChildNode;
    }

    private boolean isRunningCategory(Model model) {
        return Objects.isNull(ModelCategoryEnum.getDescritionByCode(model.getCategory()))
                || Objects.equals(model.getCategory(), ModelCategoryEnum.RUNNING.getCode());
    }

    private boolean isDesignCategory(Model model) {
        return Objects.isNull(ModelCategoryEnum.getDescritionByCode(model.getCategory())) || Objects.equals(model.getCategory(), ModelCategoryEnum.DESIGN.getCode());
    }

    private String resolveTenantId(Integer appId) {
        return resolveTenantIdByAppId(appId);
    }

    private FlowModelAppBusi resolveModelAppBusiEntity(Model model, ModelCreateVo modelCreateVo) {
        FlowModelAppBusi entity = new FlowModelAppBusi();
        BeanUtils.copyProperties(modelCreateVo, entity);
        FlowApp appInfo = flowAppRepository.detail(modelCreateVo.getAppId());
        FlowBusi busiInfo = flowBusiRepository.detail(modelCreateVo.getBusiId());
        entity.setAppName(appInfo.getAppName());
        entity.setBusiName(busiInfo.getBusiName());
        entity.setModelId(model.getId());
        entity.setModelName(model.getName());
        return entity;
    }

    @SuppressWarnings("unchecked")
    private Model saveModel(ModelCopyVo modelCopyVo) {
        User current = userRepository.getCurrentUser();
        JSONObject modelObjectNode = new JSONObject();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, modelCopyVo.getDescription());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelCopyVo.getName());
        modelObjectNode.put("creatorName", current.getUserName());
        Model model = repositoryService.newModel();

        //模型分类:设计
        model.setCategory(ModelCategoryEnum.DESIGN.getCode());
        model.setMetaInfo(modelObjectNode.toJSONString());
        model.setName(modelCopyVo.getName());
        model.setKey(modelCopyVo.getKey());
        String tenantId = resolveTenantId(modelCopyVo.getAppId());
        if (org.apache.commons.lang3.StringUtils.isEmpty(tenantId)) {
            tenantId = resolveTenantIdByAppId(modelCopyVo.getAppId());
        }
        model.setTenantId(tenantId);
        repositoryService.saveModel(model);

        String editorSource = new String(repositoryService.getModelEditorSource(modelCopyVo.getOriginModelId()), StandardCharsets.UTF_8);
        ProcessModelVo processModel = JsonUtils.toObject(editorSource, ProcessModelVo.class);
        if (Objects.nonNull(processModel) && Objects.nonNull(processModel.getProperties())) {
            Object properties = processModel.getProperties();
            if (properties instanceof HashMap) {
                ((HashMap) properties).put("process_id", modelCopyVo.getKey());
                ((HashMap) properties).put("name", modelCopyVo.getName());
                ((HashMap) properties).put("documentation", modelCopyVo.getDescription());
                List<ModelVariableVo> modelVariables = modelCopyVo.getProperties().getModelVariables();
                if (CollectionUtils.isNotEmpty(modelVariables)) {
                    ((HashMap) properties).put("modelVariables", modelVariables);
                }

            }
            processModel.setModelId(model.getId());
        }

        repositoryService.addModelEditorSource(model.getId(), JsonUtils.toJsonNotNull(processModel).getBytes(StandardCharsets.UTF_8));
        return model;
    }

    private Model saveModel(ModelCreateVo modelCreateVo) {
        User current = userRepository.getCurrentUser();
        JSONObject modelObjectNode = new JSONObject();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, modelCreateVo.getDescription());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelCreateVo.getName());
        modelObjectNode.put("creatorName", current.getUserName());
        Model model = repositoryService.newModel();

        //模型分类：设计FlowFormRepositoryImpl
        model.setCategory(ModelCategoryEnum.DESIGN.getCode());
        model.setMetaInfo(modelObjectNode.toJSONString());
        model.setName(modelCreateVo.getName());
        model.setKey(modelCreateVo.getKey());
        String tenantId = resolveTenantId(modelCreateVo.getAppId());
        if (org.apache.commons.lang3.StringUtils.isEmpty(tenantId)) {
            tenantId = resolveTenantIdByAppId(modelCreateVo.getAppId());
        }
        model.setTenantId(tenantId);
        repositoryService.saveModel(model);

        // 修改初始化默认模型数据
        String modelJson = initialModelJson(modelCreateVo, model);
        repositoryService.addModelEditorSource(model.getId(), modelJson.getBytes(StandardCharsets.UTF_8));
        return model;
    }

    private final String CREATEREQUESTURL = "/model/create";
    private final String EDITREQUESTURL = "/model/save";
    private final String DELETEREQUESTURL = "/model/delete";
    private final String OFFLINEREQUESTURL = "/model/offline";
    private final String DEPLOYREQUESTURL = "/model/deploy";
    private final String COPYREQUESTURL = "/model/copy";

    private final String CREATEOPERATIONMETHOD = "com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web.FlowModelController.createModel()";
    private final String EDITOPERATIONMETHOD = "com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web.FlowModelController.update()";
    private final String DELETEOPERATIONMETHOD = "com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web.FlowModelController.delete()";
    private final String OFFLINEOPERATIONMETHOD = "com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web.FlowModelController.offline()";
    private final String DEPLOYOPERATIONMETHOD = "com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web.FlowModelController.deploy()";
    private final String COPYPERATIONMETHOD = "com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web.FlowModelController.copy()";

    private void newRuleActionAuditLog(Model model, ModelCreateVo modelCreateVo, ObjectNode modelNode, String status) {
        //新增操作监控
        commonRuleActionAuditLog(model, "创建", ModelCategoryEnum.DESIGN.getDescription(), "",
                JSONObject.toJSON(modelCreateVo).toString(), CREATEOPERATIONMETHOD, "Post", CREATEREQUESTURL,
                status, modelNode.toString());
    }

    private void editRuleActionAuditLog(Model model, String modelStatus, String newModelId, ModelCreateVo modelCreateVo, String status) {
        //编辑操作监控
        commonRuleActionAuditLog(model, "编辑", modelStatus, newModelId,
                JSONObject.toJSON(modelCreateVo).toString(), EDITOPERATIONMETHOD, "Post", EDITREQUESTURL,
                status, JSONObject.toJSON(model).toString());
    }

    private void deleteRuleActionAuditLog(Model model, String modelStatus, String requestParam, Boolean responseResult, String status) {
        //删除操作监控
        commonRuleActionAuditLog(model, "删除", modelStatus, "",
                "{\"modelId\":\"" + requestParam + "\"}", DELETEOPERATIONMETHOD, "Post", DELETEREQUESTURL,
                status, responseResult.toString());
        offlineDelModelSendMessage(model, MqProcessTypeEnum.DELETE.getType());
    }

    private void offlineRuleActionAuditLog(Model model, String modelStatus, String requestParam, Boolean responseResult, String status) {
        //删除操作监控
        commonRuleActionAuditLog(model, "停用", modelStatus, "",
                "{\"modelId\":\"" + requestParam + "\"}", OFFLINEOPERATIONMETHOD, "Post", OFFLINEREQUESTURL,
                status, responseResult.toString());
        offlineDelModelSendMessage(model, MqProcessTypeEnum.OFFLINE.getType());
    }

    private void deployRuleActionAuditLog(Model model, String modelStatus, String requestParam, Deployment deployment, String status) {
        //部署操作监控
        commonRuleActionAuditLog(model, "部署", modelStatus, "",
                "{\"modelId\":\"" + requestParam + "\"}", DEPLOYOPERATIONMETHOD, "Post", DEPLOYREQUESTURL,
                status, JSONObject.toJSON(deployment).toString());
    }

    private void copyRuleActionAuditLog(Model model, ModelCopyVo modelCopyVo, ObjectNode modelNode, String status) {
        //新增操作监控
        commonRuleActionAuditLog(model, "复制", ModelCategoryEnum.RUNNING.getDescription(), "",
                JSONObject.toJSON(modelCopyVo).toString(), COPYPERATIONMETHOD, "Post", COPYREQUESTURL,
                status, modelNode.toString());
    }

    @Override
    public void commonRuleActionAuditLog(Model model, String action, String modelStatus, String newModelId,
                                          String requestParam, String operationMethod, String requestMethod, String requestUrl,
                                          String status, String responseResult) {
        //添加规格操作监控-新增
        User current = userRepository.getCurrentUser();
        FlowRuleActionAuditLog record = new FlowRuleActionAuditLog();
        if (model != null) {
            FlowModelAppBusi flowModelAppBusiInfo = flowModelAppBusiRepository.getAppByModelId(model.getId());
            record.setAppName(flowModelAppBusiInfo.getAppName());
            record.setBusinessName(flowModelAppBusiInfo.getBusiName());
            record.setAppId(flowModelAppBusiInfo.getAppId());
            record.setBusiId(flowModelAppBusiInfo.getBusiId());
            record.setModelId(model.getId());
            record.setModelName(flowModelAppBusiInfo.getModelName());
            //根据应用ID获取租户ID
            FlowAppInfo flowAppInfo = flowAppInfoDAO.selectByPrimaryKey(flowModelAppBusiInfo.getAppId());
            if (Objects.nonNull(flowAppInfo)) {
                record.setTenantId(flowAppInfo.getTenantId());
                FlowTenantInfo flowTenantInfo = flowTenantInfoDAO.selectByPrimaryKey(flowAppInfo.getTenantId());
                if (Objects.nonNull(flowTenantInfo)) {
                    record.setTenantName(flowTenantInfo.getTenantName());
                }
            }
            record.setVersion(String.valueOf(model.getVersion()));
            record.setModelStatus(modelStatus);
        }
        record.setAction(action);
        record.setOperator(current.getUserName());
        record.setNewModelId(newModelId);
        Date now = new Date();
        record.setOperatingTime(now);
        record.setCreateTime(now);
        record.setUpdateTime(now);
        record.setRequestParam(requestParam);
        record.setOperationMethod(operationMethod);
        record.setRequestMethod(requestMethod);
        record.setRequestUrl(requestUrl);
        record.setStatus(status);
        record.setResponseResult(responseResult);
        flowRuleActionAuditLogDAO.insertSelective(record);
    }

    private String resolveTenantIdByAppId(Integer appId) {
        FlowApp detail = flowAppRepository.detail(appId);
        if (Objects.nonNull(detail)) {
            return detail.getTenantId().toString();
        }
        return null;
    }

    private String initialModelJson(ModelCreateVo modelCreateVo, Model model) {
        ObjectMapper objectMapper = JsonUtils.getMapper();
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("modelId", model.getId());
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
        stencilSetNode.put("url", "../editor/stencilsets/bpmn2.0/bpmn2.0.json");
        editorNode.set("stencilset", stencilSetNode);
        ObjectNode stencilNode = objectMapper.createObjectNode();
        stencilNode.put("id", "BPMNDiagram");
        editorNode.set("stencil", stencilNode);
        ObjectNode propertiesNode = objectMapper.createObjectNode();
        propertiesNode.put("process_id", modelCreateVo.getKey());
        propertiesNode.put("name", modelCreateVo.getName());
        propertiesNode.put("documentation", modelCreateVo.getDescription());
        ArrayNode arrayNode = objectMapper.createArrayNode();
        if (Objects.nonNull(modelCreateVo.getProperties())) {
            for (ModelVariableVo variableVo : modelCreateVo.getProperties().getModelVariables()) {
                arrayNode.add(variableVo.toJsonNode());
            }
            propertiesNode.set("modelVariables", arrayNode);
        }
        editorNode.set("properties", propertiesNode);

        ArrayNode childShapeArray = objectMapper.createArrayNode();
        editorNode.set("childShapes", childShapeArray);

        return editorNode.toString();
    }

    private void offlineDelModelSendMessage(Model model, String TAG) {
        ProcessItem processItem = new ProcessItem();
        processItem.setDeployId(model.getDeploymentId());//部署id
        processItem.setProcessInstanceName(model.getName());//流程名称
        processItem.setProcessInstanceKey(model.getKey());//流程编码
        processItem.setProcessInstanceVersion(String.valueOf(model.getVersion()));//流程版本
//        JSONObject jsonObject = JSONObject.parseObject(model.getMetaInfo());
//        if (Objects.nonNull(jsonObject)) {
//            processItem.setApplyUserName(jsonObject.getString("creatorName"));//发起人名称
//        }
        User user = userRepository.getCurrentUser();
        processItem.setApplyUserId(user.getUserCode());//发起人id
        processItem.setApplyUserName(user.getUserName());//发起人名称
        try {
            MqMessageContext messageContext = MqMessageContext.builder().payload(MqPayload.make(processItem))
                    .tag(TAG)
                    .messageKey(model.getId())
                    .build();
            //这里可以走异步，前面拼接参数还是得同步
            doSendMessage(messageContext);
        } catch (Throwable e) {
            log.error("build messageContext error:", e);
        }
    }

    private void doSendMessage(MqMessageContext messageContext) {
        MqProducer producer = SpringUtils.getBean(MqProducer.class);
        producer.sendMessage(messageContext);
    }

    @Override
    public ResponseResult getModelEvents(ModelEventTempQuery modelEventTempQuery) {
        PageHelper.startPage(modelEventTempQuery.getPageNum(), modelEventTempQuery.getPageSize());
        List<String> modelIds = flowModelQueryDAO.getModelIdFromTemp(modelEventTempQuery);
        PageInfo<String> pageInfo = new PageInfo<>(modelIds);
        List<FlowEventTempVO> result = new ArrayList<>();
        int totalCount = (int) pageInfo.getTotal();
        for (int i = 0; i < modelIds.size(); i++) {
            modelEventTempQuery.setModelId(modelIds.get(i));
            PageHelper.startPage(1, 1000);
            List<FlowEventTemp> temp = flowModelQueryDAO.selectEventTemp(modelEventTempQuery);
            if (temp.size() > 0) {
                FlowEventTempVO flowEventTempVO = new FlowEventTempVO();
                flowEventTempVO.setId(UUIDGenerator.generate());
                flowEventTempVO.setModelId(temp.get(0).getModelId());
                flowEventTempVO.setModelKey(temp.get(0).getModelKey());
                flowEventTempVO.setModelName(temp.get(0).getModelName());
                flowEventTempVO.setCreateTime(temp.get(0).getCreateTime());
                flowEventTempVO.setChildren(temp);
                result.add(flowEventTempVO);
            }
        }
        ResponseResult responseResult = ResponseResult.success(result);
        responseResult.setTotalCount(totalCount);
        responseResult.setPageNum(modelEventTempQuery.getPageNum());
        responseResult.setPageSize(modelEventTempQuery.getPageSize());
        return responseResult;
    }

    @Override
    public ResponseResult setModelEventsToTemp() {
        ResponseResult responseResult = ResponseResult.success();
        try {
            List<FlowModelQueryInfo> models = new ArrayList<>();
            models = flowModelQueryDAO.selectModelList();  //获取最新版的模型
            for (int i = 0; i < models.size(); i++) {
                FlowModelQueryInfo flowModelQueryInfo = models.get(i);
                if (flowModelQueryInfo != null) {
                    String modelId = flowModelQueryInfo.getModelId();
                    Date lastUpdateTime = flowModelQueryInfo.getLastUpdateTime();
                    if (StringUtils.isNotBlank(modelId) && lastUpdateTime != null && checkIsNeedToUpdate(modelId, lastUpdateTime)) {
                        handleModelEventsByModelId(modelId);
                    }
                }
            }
        } catch (Exception e) {
            responseResult = ResponseResult.fail(-1, e.getMessage());
        }
        return responseResult;
    }

    //如果临时事件表中事件的创建事件大于等于最后一次更新时间
    public Boolean checkIsNeedToUpdate(String modelId, Date lastUpdateTime) {
        int num = flowModelQueryDAO.selectEventsByModelIdAndDate(modelId, lastUpdateTime);
        if (num > 0) {
            return false;
        }
        int tempNum = flowModelQueryDAO.selectEventsByModelIdAndDate(modelId, null);
        if (tempNum > 0) {
            flowModelQueryDAO.deleteEventTemp(modelId);
        }
        return true;
    }

    void handleModelEventsByModelId(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if (model == null) {
            return;
        }
        byte[] modelBytes = repositoryService.getModelEditorSource(model.getId());
        logger.info("ModelId:" + model.getId() + " ModelBytes:");
        String editorSource = new String(modelBytes, StandardCharsets.UTF_8);
        logger.info(editorSource);
        JsonNode modelJson = JsonUtils.readTree(editorSource);
        try {
            // 处理 UserTask节点
            //BpmnModel bpmnModel = bpmnJsonConverter.convertToBpmnModel(modelJson);
            List<JsonNode> nodeList = processNode(modelJson);
            for (JsonNode elementNode : nodeList) {
                try {
                    FlowElementProcessContext flowElementProcessContext = FlowElementProcessContext.builder()
                            .nodeCode(elementNode.get("properties").get("overrideid").asText())
                            .modelNode(modelJson)
                            .elementNode(elementNode)
                            .build();
                    insertEventsToTemp(flowElementProcessContext);
                } catch (Exception e) {
                    logger.info("executeStartEndPropertiesProcessor Error,Reason:" + e.getLocalizedMessage());
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            log.info("{}", e.getMessage());
        }
    }

    public void insertEventsToTemp(FlowElementProcessContext context) {
        List<FlowEventTemp> eventConfig = resolveEventConfig(context);
        for (FlowEventTemp config : eventConfig) {
            if (StringUtils.isNotBlank(config.getModelId())) {
                FlowModelQueryInfo flowModelQueryInfo = flowModelQueryDAO.selectModelByModelId(config.getModelId());
                if (flowModelQueryInfo != null) {
                    config.setCreateTime(flowModelQueryInfo.getCreateTime());
                }
            }
            logger.info("插入flow_events_temp的数据：{}", config);
            flowModelQueryDAO.insertEventTemp(config);
        }
    }

    private List<FlowEventTemp> resolveEventConfig(FlowElementProcessContext context) {
        List<FlowEventTemp> result = new ArrayList<>();
        JsonNode eventConfigNode = context.getElementNode().get("properties").get("eventConfig");
        String nodeName = context.getElementNode().get("properties").get("name").toString();
        List<NodeEventConfigRawDTO> nodeEventConfigRawDTOS = JsonUtils.toObject(
                JsonUtils.toJson(eventConfigNode), new com.fasterxml.jackson.core.type.TypeReference<List<NodeEventConfigRawDTO>>() {
                });
        if (nodeEventConfigRawDTOS == null) {
            return result;
        }
        String multiinstanceType = null;
        if (context.getElementNode().get("properties").has("multiinstance_type")) {
            multiinstanceType = context.getElementNode().get("properties").get("multiinstance_type").textValue();
        }
        for (NodeEventConfigRawDTO node : nodeEventConfigRawDTOS) {
            FlowEventTemp flowEventTemp = new FlowEventTemp();
            flowEventTemp.setName(node.getName());
            if (nodeName.startsWith("\"") && nodeName.endsWith("\"")) {
                flowEventTemp.setNodeName(nodeName.substring(1, nodeName.length() - 1));
            } else {
                flowEventTemp.setNodeName(nodeName);
            }
            flowEventTemp.setDescription(node.getDesc());
            if ("Parallel".equalsIgnoreCase(multiinstanceType) || "Sequential".equalsIgnoreCase(multiinstanceType)) {
                String eventType = node.getEventType();
                if (Objects.equals(eventType, FlowableEngineEventType.TASK_CREATED.name())) {
                    flowEventTemp.setEventType(FlowableEngineEventType.MULTI_INSTANCE_ACTIVITY_STARTED.name());
                } else if (Objects.equals(eventType, FlowableEngineEventType.TASK_COMPLETED.name())) {
                    flowEventTemp.setEventType(FlowableEngineEventType.MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION.name());
                } else {
                    flowEventTemp.setEventType(eventType);
                }
            } else {
                flowEventTemp.setEventType(node.getEventType());
            }
            Map.Entry<String, String> config = node.getConfig();
            if (Objects.nonNull(config)) {
                flowEventTemp.setConfigType(config.getKey());
                flowEventTemp.setConfigContent(config.getValue());
            }
            String callMethod = "sync";
            if (org.apache.commons.lang3.StringUtils.isNotBlank(node.getCallMethod())) {
                callMethod = node.getCallMethod();
            }
            flowEventTemp.setCallMethod(callMethod);
            flowEventTemp.setRequestBody(node.getRequestBody());
            flowEventTemp.setModelId(context.getModelNode().get("modelId").textValue());
            flowEventTemp.setModelKey(context.getModelNode().get("properties").get("process_id").textValue());
            flowEventTemp.setModelName(context.getModelNode().get("properties").get("name").textValue());
            flowEventTemp.setNodeCode(context.getNodeCode());
            Date date = new Date();
            flowEventTemp.setGmtCreate(date);
            if (org.apache.commons.lang3.StringUtils.isNotBlank(flowEventTemp.getConfigContent())) {
                result.add(flowEventTemp);
            }
        }
        return result;
    }

    /**
     * 解析出"开始""任务"和"结束"节点
     *
     * @param modelNode 整个模型节点
     * @return 开始+任务+结束 节点
     */
    public List<JsonNode> processNode(JsonNode modelNode) {
        List<JsonNode> nodeList = new ArrayList<>();
        if (modelNode != null && modelNode.get("childShapes") != null) {
            Iterator childShapes = modelNode.get("childShapes").iterator();
            JsonNode jsonChildNode;
            String stencilId;
            while (childShapes.hasNext()) {
                jsonChildNode = (JsonNode) childShapes.next();
                stencilId = BpmnJsonConverterUtil.getStencilId(jsonChildNode);
                logger.info("{}***{}", stencilId, jsonChildNode);
                if (("StartNoneEvent".equals(stencilId) || "EndNoneEvent".equals(stencilId)) || "UserTask".equals(stencilId)) {
                    nodeList.add(jsonChildNode);
                }
            }
        }
        return nodeList;
    }

    public String transferModelKeyToId(String modelKey) {
        List<FlowModelQueryInfo> result = flowModelQueryDAO.selectModelListByKey(modelKey);
        if (CollectionUtils.isNotEmpty(result)) {
            return result.get(0).getModelId();
        }
        return null;
    }

    /**
     * 根据modelKey获取运行中的模板信息
     * @param modelKey
     * @return
     */
    @Override
    public ResponseResult getModelByModelKey(String modelKey){
        ResponseResult responseResult = ResponseResult.success();
        try {
            List<FlowModelQueryInfo> result = flowModelQueryDAO.selectModelListByKey(modelKey);
            if (CollectionUtils.isNotEmpty(result)) {
                responseResult.setDatas(result.get(0));
            }
        } catch (Exception e) {
            responseResult = ResponseResult.fail(-1, e.getMessage());
        }
        return responseResult;
    }

}
